package com.letoken.platform.app.consumer;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.letoken.platform.app.service.AppFundInfoService;
import com.letoken.platform.app.service.AppUserAssetsHistoryService;
import com.letoken.platform.app.service.AppUserLampService;
import com.letoken.platform.app.service.AppUserWalletService;
import com.letoken.platform.pub.enums.LetokenTokenTypeEnum;
import com.letoken.platform.pub.enums.TokenTransactionStatusEnum;
import com.letoken.platform.pub.enums.TokenTransactionTypeEnum;
import com.letoken.platform.pub.exception.ApiServiceException;
import com.letoken.platform.repo.admin.dao.AdminSameConfigMapper;
import com.letoken.platform.repo.admin.po.AdminSameConfig;
import com.letoken.platform.repo.app.dao.*;
import com.letoken.platform.repo.app.po.*;
import com.letoken.platform.repo.app.req.user.QueueWeightingReq;
import com.letoken.platform.repo.app.req.user.UserLevelReq;
import com.letoken.platform.repo.helper.CalculateConsumerValue;
import com.letoken.platform.repo.mall.dao.AdminMallAmbassadorMapper;
import com.letoken.platform.repo.mall.po.AdminMallAmbassador;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Component
@Slf4j
public class FundConsumer {



    public final static String exchange = "zletoken.fund";
    public final static String routingKey = "fund.#";
    public final static String queueBonus = "zfund.bonus";
    public final static String queueWeighting = "zfund.weighting";
    @Bean
    public Queue bonusqueue() {
        return new Queue(queueBonus);
    }

    @Bean
    public Queue weightingQueue() {
        return new Queue(queueWeighting);
    }

    @Bean
    public TopicExchange fundExchange() {
        return new TopicExchange(exchange);
    }

    @Bean
    public Binding queueBindingExchange(@Qualifier("bonusqueue") Queue bonusqueue,
                                        @Qualifier("fundExchange") TopicExchange exchange){
        return BindingBuilder.bind(bonusqueue).to(exchange).with(queueBonus);
    }


    @Bean
    public Binding bindingweighting(@Qualifier("weightingQueue") Queue weightingQueue,
                                         @Qualifier("fundExchange") TopicExchange exchange) {
        return BindingBuilder.bind(weightingQueue).to(exchange).with(queueWeighting);
    }


    @Resource
    private AppFundInfoMapper appFundInfoMapper;

    @Autowired
    private AppUserAssetsHistoryService appUserAssetsHistoryService;

    @Autowired
    private AppUserWalletService appUserWalletService;

    @Resource
    private AdminMallAmbassadorMapper adminMallAmbassadorMapper;

    @Resource
    AppUserAssetsHistoryMapper appUserAssetsHistoryMapper;

    @Resource
    AppUserWalletMapper appUserWalletMapper;


    @Resource
    AdminFundLevelConfigMapper adminFundLevelConfigMapper;

    @Autowired
    CalculateConsumerValue calculateConsumerValue;

    @Resource
    AppFundV2InfoMapper appFundV2InfoMapper;

    @Resource
    AppUserTeamMapper appUserTeamMapper;

    @Resource
    AppFundLevelInfoMapper appFundLevelInfoMapper;

    @Autowired
    AppUserLampService appUserLampService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    AppFundInfoService appFundInfoService;

    @Resource
    AdminSameConfigMapper adminSameConfigMapper;

    /**
     * 团队奖励
     * @param message
     */
    @RabbitListener(queues = FundConsumer.queueBonus)
    @RabbitHandler
    @Transactional
    public void bonus(@Payload String message) {
        log.info("进入团队奖励 奖励信息:"+message);
        UserLevelReq userLevelReq = new UserLevelReq();
        try {
            BonusDto dto = JSONObject.parseObject(message, BonusDto.class);
            BigDecimal amount = dto.getAmount();
            BigDecimal rate =  appFundInfoMapper.getMaxfundRate();
            List<UserLevelReq> list = appFundInfoMapper.selectUserLevel(dto.getUserId());
            Integer level = 0;
            BigDecimal levelRate = BigDecimal.ZERO;
            List<AdminFundLevelConfig> configList = adminFundLevelConfigMapper.getList();
            if(list.size()>0){
                for (UserLevelReq req:list) {
                    userLevelReq = req;
                    BigDecimal  bigDecimal = appFundInfoMapper.sumAmount(req.getUserId(),1);
                    if(req.getLevel() > level && rate.compareTo(BigDecimal.ZERO) > 0){
                        BigDecimal rates= req.getRate();
                        req.setRate(req.getRate().subtract(levelRate));
                        BigDecimal ss = rate.compareTo(req.getRate())>0?req.getRate():rate;
                        BigDecimal reward = amount.multiply(ss);
                        rate = rate.compareTo(req.getRate())>0?rate.subtract(req.getRate()):BigDecimal.ZERO;
                        level = req.getLevel();
                        levelRate = rates;
                        BigDecimal sss = configList.stream().filter(s->s.getLevel().equals(req.getLevel())).collect(Collectors.toList()).get(0).getFundAmount();
                        if(ObjectUtils.isEmpty(bigDecimal)  ||  bigDecimal.compareTo(sss)<0)continue;

                        if (reward.compareTo(BigDecimal.ZERO)<=0) continue;
                        AppFundV2Info appFundV2Info = appFundV2InfoMapper.selectBYUserId(req.getUserId());
                        if(ObjectUtils.isEmpty(appFundV2Info) || appFundV2Info.getRetained().compareTo(BigDecimal.ZERO) == 0)continue;
                        if(ObjectUtils.isEmpty(appFundV2Info) || appFundV2Info.getRetained().compareTo(req.getFundAmount()) < 0){
                            log.info("服务奖：用户{}不满足服务奖留存金额", req.getUserId());
                            continue;
                        }


                        if(reward.compareTo(appFundV2Info.getRetained()) > 0){
                            reward = appFundV2Info.getRetained();
                        }


                        AppFundV2Info appFundV2Info1 = appFundInfoService.updateInfo(appFundV2Info.getUserId(),reward,BigDecimal.ZERO);
                        /*AppFundV2Info appFundV2Info1 = new AppFundV2Info();
                        appFundV2Info1.setUserId(appFundV2Info.getUserId());
                        appFundV2Info1.setRetained(appFundV2Info.getRetained().subtract(reward));
                        appFundV2Info1.updateById();*/

                        AppFundV2Detail appFundV2Detail = new AppFundV2Detail();
                        appFundV2Detail.setAmount(reward);
                        appFundV2Detail.setUserId(req.getUserId());
                        appFundV2Detail.setCreateTime(LocalDateTime.now());
                        appFundV2Detail.setAfterAmount(appFundV2Info1.getRetained());
                        appFundV2Detail.setType(3);
                        appFundV2Detail.setSourceUserId(dto.getSourceUserId());
                        appFundV2Detail.insert();


                        appUserWalletService.updateWallet(LetokenTokenTypeEnum.LET.getId(),req.getUserId(),reward,TokenTransactionTypeEnum.FUND_BONUS.getValue(),"");


                        /*Integer walletId = appUserWalletMapper.checkoutTokenIdAndUserId(LetokenTokenTypeEnum.LET.getId(), req.getUserId());
                        AppUserWallet w = appUserWalletMapper.selectForUpdate(walletId);
                        AppUserWallet updateW = new AppUserWallet();
                        updateW.setId(w.getId());
                        updateW.setAvailableBalance(w.getAvailableBalance().add(reward));
                        appUserWalletMapper.updateById(updateW);

                        AppUserAssetsHistory assetHistory = new AppUserAssetsHistory();
                        assetHistory.setUserId(req.getUserId());
                        assetHistory.setType(TokenTransactionTypeEnum.FUND_BONUS.getValue());
                        assetHistory.setStatus(TokenTransactionStatusEnum.COMPLETED.getValue());
                        assetHistory.setTokenId(LetokenTokenTypeEnum.LET.getId());
                        assetHistory.setAmount(reward);
                        assetHistory.setBalance(updateW.getAvailableBalance());
                        assetHistory.setCreateTime(LocalDateTime.now());
                        assetHistory.setUpdateTime(assetHistory.getCreateTime());
                        appUserAssetsHistoryMapper.insert(assetHistory);*/
                        //appUserLampService.updateLamd(appFundV2Info.getUserId(),reward.divide(new BigDecimal(5)).negate());
                        fundRelease(appFundV2Info.getUserId(),reward);
                        if(req.getLevel().equals(5)){
                           List<AdminSameConfig> sameConfigList = adminSameConfigMapper.selectList( new QueryWrapper<>());
                           Integer id = req.getUserId();
                            for (int i = 1; i <= sameConfigList.size(); i++) {
                                if(id !=null && !id.equals(0)){
                                    int finalI = i;
                                    id = equalLevelAward(id,sss,reward,sameConfigList.stream().filter(s -> s.getId().equals(finalI)).collect(Collectors.toList()).get(0).getRate());
                                }else break;
                            }

                        }
                        /*AppUserAssetsHistory appUserAssetsHistory = new AppUserAssetsHistory();
                        appUserAssetsHistory.setUserId(req.getUserId());
                        appUserAssetsHistory.setType(TokenTransactionTypeEnum.FUND_BONUS.getValue());
                        appUserAssetsHistory.setStatus(1);
                        appUserAssetsHistory.setSn(req.getUserId().toString());
                        appUserAssetsHistory.setTokenId(2);
                        appUserAssetsHistory.setAmount(reward);
                        appUserAssetsHistory.setCreateTime(LocalDateTime.now());
                        appUserAssetsHistory.setUpdateTime(LocalDateTime.now());
                        boolean saveHistory = appUserAssetsHistoryService.save(appUserAssetsHistory);
                        if (!saveHistory) {
                            throw new ApiServiceException(500, "mint失败，请稍后再试！");
                        }

                        AppFundDetail appFundDetail = new AppFundDetail();
                        appFundDetail.setUserId(req.getUserId());
                        appFundDetail.setType(2);
                        appFundDetail.setAmount(reward);
                        appFundDetail.setCreateTime(LocalDateTime.now());
                        appFundDetail.insert();
                        int updateWallet = appUserWalletService.updateUserAmountByUserIDandCoin(reward, new BigDecimal("0"),BigDecimal.ZERO, req.getUserId(), LetokenTokenTypeEnum.LET.getId());
                        if (updateWallet == 0) {
                            throw new ApiServiceException(500, "mint失败，请稍后再试！");
                        }
                        try {
                            AppUserAssetsHistory updateAssetHistory = new AppUserAssetsHistory();
                            updateAssetHistory.setBalance(appUserWalletMapper.getAvailableBalance(2, req.getUserId()));
                            updateAssetHistory.setId(appUserAssetsHistory.getId());
                            updateAssetHistory.updateById();
                        }catch (Exception e){
                            log.error("更新余额失败 id{} eMsg{}", appUserAssetsHistory.getId(), e.getMessage());
                        }*/


                    }
                }
            }
        } catch (Exception ex) {
            log.error("MQ >> 团队奖励执行失败: 奖励信息"+message+"  失败用户信息:"+JSONObject.toJSONString(userLevelReq)+"      错误信息:"+ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 加权奖励
     * @param message
     */
    @RabbitListener(queues = FundConsumer.queueWeighting)
    @RabbitHandler
    public void weighting(@Payload String message) {
        log.info("进入加权奖励:"+message);
        BigDecimal amount = new BigDecimal(message);
        Integer sss=1;
        try {
            List<AdminFundLevelConfig> ambassadors = adminFundLevelConfigMapper.getWeightDate();
            List<QueueWeightingReq> updateList = new ArrayList<>();
            for (AdminFundLevelConfig ambassador:ambassadors) {
                List<QueueWeightingReq> list = appFundInfoMapper.getUserByLevel(ambassador.getLevel());
                if(list == null || list.size()==0)continue;
                BigDecimal money = amount.multiply(ambassador.getWeightRate()).divide(new BigDecimal(list.size()),10, RoundingMode.HALF_UP);

                for (QueueWeightingReq req:list) {
                    BigDecimal bigDecimal = appFundInfoMapper.sumAmount(req.getUserId(),3);
                    BigDecimal ss = ambassadors.stream().filter(s->s.getLevel().equals(req.getLevel())).collect(Collectors.toList()).get(0).getWeightAmount();
                    if(ObjectUtils.isEmpty(bigDecimal) || bigDecimal.compareTo(ss)<0)continue;
                    boolean found = false;
                    for (QueueWeightingReq reqs : updateList) {
                        if (req.getUserId().equals(reqs.getUserId())) {
                            reqs.setAmount(reqs.getAmount().add(money));
                            found = true;
                            break;
                        }
                    }

                    if (!found) {
                        QueueWeightingReq queueWeightingReq = new QueueWeightingReq();
                        queueWeightingReq.setUserId(req.getUserId());
                        queueWeightingReq.setAmount(money);
                        updateList.add(queueWeightingReq);
                    }

                }
            }

            for (QueueWeightingReq req:updateList) {
                sss = req.getUserId();
                log.info("用户发放加权奖励:"+req.getUserId());
                if (!calculateConsumerValue.hasKey(calculateConsumerValue.getKey(), String.valueOf(req.getUserId())) && !calculateConsumerValue.isNewUser(req.getUserId())){
                    log.info("基金加权分红：用户{}不满足消费值条件",req.getUserId());
                    continue;
                }


                AppFundV2Info appFundV2Info = appFundV2InfoMapper.selectBYUserId(req.getUserId());
                if(ObjectUtils.isEmpty(appFundV2Info) || appFundV2Info.getRetained().compareTo(BigDecimal.ZERO) == 0)continue;

                if(ObjectUtils.isEmpty(appFundV2Info) || appFundV2Info.getRetained().compareTo(req.getAmount()) < 0){
                    log.info("服务奖：用户{}不满足服务奖留存金额", req.getUserId());
                    continue;
                }
                BigDecimal reward = req.getAmount();
                if(reward.compareTo(appFundV2Info.getRetained()) > 0){
                    reward = appFundV2Info.getRetained();
                }


                AppFundV2Info appFundV2Info1 = appFundInfoService.updateInfo(appFundV2Info.getUserId(),reward,BigDecimal.ZERO);

                /*AppFundV2Info appFundV2Info1 = new AppFundV2Info();
                appFundV2Info1.setUserId(appFundV2Info.getUserId());
                appFundV2Info1.setRetained(appFundV2Info.getRetained().subtract(reward));
                appFundV2Info1.updateById();*/

                AppFundV2Detail appFundV2Detail = new AppFundV2Detail();
                appFundV2Detail.setAmount(reward);
                appFundV2Detail.setUserId(req.getUserId());
                appFundV2Detail.setCreateTime(LocalDateTime.now());
                appFundV2Detail.setAfterAmount(appFundV2Info1.getRetained());
                appFundV2Detail.setType(4);
                appFundV2Detail.insert();
                appUserWalletService.updateWallet(LetokenTokenTypeEnum.LET.getId(),req.getUserId(),reward,TokenTransactionTypeEnum.FUND_WEIGHTING.getValue(),"");

               /* Integer walletId = appUserWalletMapper.checkoutTokenIdAndUserId(LetokenTokenTypeEnum.LET.getId(), req.getUserId());
                AppUserWallet w = appUserWalletMapper.selectForUpdate(walletId);
                AppUserWallet updateW = new AppUserWallet();
                updateW.setId(w.getId());
                updateW.setAvailableBalance(w.getAvailableBalance().add(req.getAmount()));
                appUserWalletMapper.updateById(updateW);

                AppUserAssetsHistory assetHistory = new AppUserAssetsHistory();
                assetHistory.setUserId(req.getUserId());
                assetHistory.setType(TokenTransactionTypeEnum.FUND_WEIGHTING.getValue());
                assetHistory.setStatus(TokenTransactionStatusEnum.COMPLETED.getValue());
                assetHistory.setTokenId(LetokenTokenTypeEnum.LET.getId());
                assetHistory.setAmount(reward);
                assetHistory.setBalance(updateW.getAvailableBalance());
                assetHistory.setCreateTime(LocalDateTime.now());
                assetHistory.setUpdateTime(assetHistory.getCreateTime());
                appUserAssetsHistoryMapper.insert(assetHistory);*/
                //appUserLampService.updateLamd(appFundV2Info.getUserId(),reward.divide(new BigDecimal(5)).negate());
                fundRelease(appFundV2Info.getUserId(),reward);

                /*Integer walletId = appUserWalletMapper.checkoutTokenIdAndUserId(LetokenTokenTypeEnum.LET.getId(), req.getUserId());
                AppUserWallet w = appUserWalletMapper.selectForUpdate(walletId);
                AppUserWallet updateW = new AppUserWallet();
                updateW.setId(w.getId());
                updateW.setAvailableBalance(w.getAvailableBalance().add(req.getAmount()));
                appUserWalletMapper.updateById(updateW);

                AppUserAssetsHistory assetHistory = new AppUserAssetsHistory();
                assetHistory.setUserId(req.getUserId());
                assetHistory.setType(TokenTransactionTypeEnum.FUND_WEIGHTING.getValue());
                assetHistory.setStatus(TokenTransactionStatusEnum.COMPLETED.getValue());
                assetHistory.setTokenId(LetokenTokenTypeEnum.LET.getId());
                assetHistory.setAmount(req.getAmount());
                assetHistory.setBalance(updateW.getAvailableBalance());
                assetHistory.setCreateTime(LocalDateTime.now());
                assetHistory.setUpdateTime(assetHistory.getCreateTime());
                appUserAssetsHistoryMapper.insert(assetHistory);

                AppFundDetail appFundDetail = new AppFundDetail();
                appFundDetail.setUserId(req.getUserId());
                appFundDetail.setType(3);
                appFundDetail.setAmount(req.getAmount());
                appFundDetail.setCreateTime(LocalDateTime.now());
                appFundDetail.insert();*/
                log.info("结束发放加权奖励:"+req.getUserId());
            }
        } catch (Exception ex) {
            log.error("MQ >> 加权奖励执行失败:"+ex+"  失败用户id:"+sss);
        }
        log.info("加权奖励执行完成");
    }
    /*@RabbitListener(queues = FundConsumer.queueWeighting)
    @RabbitHandler
    public void weighting(@Payload String message) {
       BigDecimal amount = new BigDecimal(message);
        try {
           List<QueueWeightingReq> list = appFundInfoMapper.getCountByLevel();
           List<AdminMallAmbassador> ambassadors = adminMallAmbassadorMapper.selectList(new QueryWrapper<>());
            for (QueueWeightingReq queueWeightingReq:list) {
                BigDecimal rate = ambassadors.stream().filter(a -> a.getLevel().equals(queueWeightingReq.getLevel())).collect(Collectors.toList()).get(0).getWeightRate();
                List<Integer> ids = appFundInfoMapper.getUserIdByLevel(queueWeightingReq.getLevel());
                BigDecimal money = amount.multiply(rate).divide(new BigDecimal(queueWeightingReq.getCount()),10, RoundingMode.HALF_UP);
                for (Integer id:ids) {
                    AppUserWallet w = appUserWalletMapper.selectForUpdate(id);
                    AppUserWallet updateW = new AppUserWallet();
                    updateW.setId(w.getId());
                    updateW.setAvailableBalance(w.getAvailableBalance().subtract(money));
                    appUserWalletMapper.updateById(updateW);

                    AppUserAssetsHistory assetHistory = new AppUserAssetsHistory();
                    assetHistory.setUserId(id);
                    assetHistory.setType(TokenTransactionTypeEnum.FUND_WEIGHTING.getValue());
                    assetHistory.setStatus(TokenTransactionStatusEnum.COMPLETED.getValue());
                    assetHistory.setTokenId(LetokenTokenTypeEnum.LET.getId());
                    assetHistory.setAmount(money);
                    assetHistory.setBalance(updateW.getAvailableBalance());
                    assetHistory.setCreateTime(LocalDateTime.now());
                    assetHistory.setUpdateTime(assetHistory.getCreateTime());
                    appUserAssetsHistoryMapper.insert(assetHistory);
                }
            }
        } catch (Exception ex) {
            log.error("MQ >> imLogin error！ [{}]", ex.getMessage());
        }
    }*/


    @Data
    public static class BonusDto implements Serializable {
        private Integer userId;
        private BigDecimal amount;
        private Integer sourceUserId;
    }


    private Integer equalLevelAward(Integer userId,BigDecimal amount,BigDecimal reward,BigDecimal rate){
        Integer id = 0;
        AppUserTeam appUserTeam =  appUserTeamMapper.selectById(userId);
        if(appUserTeam.getInviterId() != 0 || appUserTeam.getInviterId() !=null){
            Integer count = appFundLevelInfoMapper.getLevelBYLevl(appUserTeam.getInviterId());
            if(count.equals(1)){
                AppFundV2Info appFundV2Info2 = appFundV2InfoMapper.selectBYUserId(appUserTeam.getInviterId());
                if(appFundV2Info2 != null && appFundV2Info2.getRetained().compareTo(amount)>=0){
                    AppFundV2Info appFundV2Info = appFundV2InfoMapper.selectBYUserId(appFundV2Info2.getUserId());
                    reward = reward.multiply(rate);
                    if (reward.compareTo(BigDecimal.ZERO)<=0) return 0;
                    if(reward.compareTo(appFundV2Info.getRetained()) > 0){
                        reward = appFundV2Info.getRetained();
                    }

                    AppFundV2Info appFundV2Info1 = appFundInfoService.updateInfo(appFundV2Info.getUserId(),reward,BigDecimal.ZERO);


                    AppFundV2Detail appFundV2Detail = new AppFundV2Detail();
                    appFundV2Detail.setAmount(reward);
                    appFundV2Detail.setUserId(appFundV2Info2.getUserId());
                    appFundV2Detail.setCreateTime(LocalDateTime.now());
                    appFundV2Detail.setAfterAmount(appFundV2Info1.getRetained());
                    appFundV2Detail.setType(5);
                    appFundV2Detail.setSourceUserId(userId);
                    appFundV2Detail.insert();

                    appUserWalletService.updateWallet(LetokenTokenTypeEnum.LET.getId(),appFundV2Info2.getUserId(),reward,TokenTransactionTypeEnum.V5.getValue(),"");

                    fundRelease(appFundV2Info.getUserId(),reward);

                    id = appUserTeam.getInviterId();
                }
            }
        }
        return id;
    }

    public void fundRelease(Integer userId,BigDecimal amount){
        FundReleaseConsumer.FundReleaseDto dto = new FundReleaseConsumer.FundReleaseDto();
        dto.setAmount(amount.negate());
        dto.setUserId(userId);
        rabbitTemplate.convertAndSend( FundReleaseConsumer.queue,  JSONObject.toJSON(dto).toString());
    }

    @Recover
    public void recover(SQLException e, String message) {
        // 重试达到最大次数后的恢复逻辑
       log.info("Database operation failed after retries for message: " + message + " Error: " + e.getMessage());
    }

}
